from optparse import make_option
import logging
import sys

from django.db import connections
from ...boot import PROJECT_DIR
from ...db.base import DatabaseWrapper, get_datastore_paths
from django.core.management.base import BaseCommand
from django.core.management.commands.runserver import BaseRunserverCommand
from django.core.exceptions import ImproperlyConfigured

from google.appengine.tools import dev_appserver_main


class Command(BaseRunserverCommand):
    """Overrides the default Django runserver command.

    Instead of starting the default Django development server this command
    fires up a copy of the full fledged App Engine dev_appserver that emulates
    the live environment your application will be deployed to.
    """

    option_list = BaseCommand.option_list + (
        make_option('--debug', action='store_true', default=False,
            help='Prints verbose debugging messages to the console while running.'),
        make_option('--debug_imports', action='store_true', default=False,
            help='Prints debugging messages related to importing modules, including \
                search paths and errors.'),
        make_option('-c', '--clear_datastore', action='store_true', default=False,
            help='Clears the datastore data and history files before starting the web server.'),
        make_option('--high_replication', action='store_true', default=False,
            help='Use the high replication datastore consistency model.'),
        make_option('--require_indexes', action='store_true', default=False,
            help="""Disables automatic generation of entries in the index.yaml file. Instead, when
                    the application makes a query that requires that its index be defined in the
                    file and the index definition is not found, an exception will be raised,
                    similar to what would happen when running on App Engine."""),
        make_option('--enable_sendmail', action='store_true', default=False,
            help='Uses the local computer\'s Sendmail installation for sending email messages.'),
        make_option('--datastore_path',
            help="""The path to use for the local datastore data file. The server creates this file
                    if it does not exist."""),
        make_option('--history_path',
            help="""The path to use for the local datastore history file. The server uses the query
                    history file to generate entries for index.yaml."""),
        make_option('--login_url',
            help='The relative URL to use for the Users sign-in page. Default is /_ah/login.'),
        make_option('--smtp_host',
            help='The hostname of the SMTP server to use for sending email messages.'),
        make_option('--smtp_port',
            help='The port number of the SMTP server to use for sending email messages.'),
        make_option('--smtp_user',
            help='The username to use with the SMTP server for sending email messages.'),
        make_option('--smtp_password',
            help='The password to use with the SMTP server for sending email messages.'),
        make_option('--use_sqlite', action='store_true', default=False,
            help='Use the new, SQLite datastore stub.'),
    )

    help = 'Runs a copy of the App Engine development server.'
    args = '[optional port number, or ipaddr:port]'

    def create_parser(self, prog_name, subcommand):
        """
        Create and return the ``OptionParser`` which will be used to
        parse the arguments to this command.

        """
        # hack __main__ so --help in dev_appserver_main works OK.
        sys.modules['__main__'] = dev_appserver_main
        return super(Command, self).create_parser(prog_name, subcommand)

    def run_from_argv(self, argv):
        """
        Captures the program name, usually "manage.py"
        """

        self.progname = argv[0]
        super(Command, self).run_from_argv(argv)

    def run(self, *args, **options):
        """
        Starts the App Engine dev_appserver program for the Django project.
        The appserver is run with default parameters. If you need to pass any special
        parameters to the dev_appserver you will have to invoke it manually.

        Unlike the normal devserver, does not use the autoreloader as
        App Engine dev_appserver needs to be run from the main thread
        """

        args = []
        # Set bind ip/port if specified.
        if self.addr:
            args.extend(["--address", self.addr])
        if self.port:
            args.extend(["--port", self.port])

        # If runserver is called using handle(), progname will not be set
        if not hasattr(self, 'progname'):
            self.progname = "manage.py"

        # Add email settings
        from django.conf import settings
        if not options.get('smtp_host', None) and not options.get('enable_sendmail', None):
            args.extend(['--smtp_host', settings.EMAIL_HOST,
                        '--smtp_port', str(settings.EMAIL_PORT),
                        '--smtp_user', settings.EMAIL_HOST_USER,
                        '--smtp_password', settings.EMAIL_HOST_PASSWORD])

        # Pass the application specific datastore location to the server.
        preset_options = {}
        for name in connections:
            connection = connections[name]
            if isinstance(connection, DatabaseWrapper):
                for key, path in get_datastore_paths(connection.settings_dict).items():
                    # XXX/TODO: Remove this when SDK 1.4.3 is released
                    if key == 'prospective_search_path':
                        continue

                    arg = '--' + key
                    if arg not in args:
                        args.extend([arg, path])
                # Get dev_appserver option presets, to be applied below
                preset_options = connection.settings_dict.get('DEV_APPSERVER_OPTIONS', {})
                break

        # Process the rest of the options here
        bool_options = ['debug', 'debug_imports', 'clear_datastore', 'require_indexes',
                        'high_replication', 'enable_sendmail', 'use_sqlite',]
        for opt in bool_options:
            if options[opt] != False:
                args.append("--%s" % opt)

        str_options = ['datastore_path', 'history_path', 'login_url', 'smtp_host', 'smtp_port',
                       'smtp_user', 'smtp_password',]
        for opt in str_options:
            if options.get(opt, None) != None:
                args.extend(["--%s" % opt, options[opt]])

        # Fill any non-overridden options with presets from settings
        for opt, value in preset_options.items():
            arg = "--%s" % opt
            if arg not in args:
                if value and opt in bool_options:
                    args.append(arg)
                elif opt in str_options:
                    args.extend([arg, value])
                # TODO: issue warning about bogus option key(s)?

        # Reset logging level to INFO as dev_appserver will spew tons of debug logs
        logging.getLogger().setLevel(logging.INFO)

        # Append the current working directory to the arguments.
        dev_appserver_main.main([self.progname] + args + [PROJECT_DIR])
